package br.edu.unifei.cct720.trabalho06.controlador.principal;

import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import br.edu.unifei.cct720.trabalho06.eventos.MyActionListener;
import br.edu.unifei.cct720.trabalho06.gui.principal.JanelaPrincipal;
import br.edu.unifei.cct720.trabalho06.padroes.Controlador;
import br.edu.unifei.cct720.trabalho06.utils.arquivo.ArquivoPoligono;
import br.edu.unifei.cct720.trabalho06.utils.constantes.Mensagem;
import br.edu.unifei.cct720.trabalho06.model.formas.Poligono;
import br.edu.unifei.cct720.trabalho06.model.formas.Reta;
import br.edu.unifei.cct720.trabalho06.model.ponto.Pontos;
import br.edu.unifei.cct720.trabalho06.model.preenchimento.Preenchimento;
import br.edu.unifei.cct720.trabalho06.gui.componentes.MyFileChooser;

/**
 * Classe responsavel por intermediar as interacoes do usuario
 * com o restante do programa
 * 
 * @author fknappe
 *
 */
public class ControladorPrincipal extends Controlador {

	// Declaracao das variaveis de instancia
	private ArquivoPoligono arquivoPoligono = null;
	private String caminhoUltimoArquivo = null;
	private Poligono poligono = null;

	/**
	 * Construtor 
	 * 
	 * @param JanelaPrincipal j
	 */
	public ControladorPrincipal(JanelaPrincipal j) {
		super(j);
	}

	/**
	 * Metodo responsavel por registrar todas as acoes
	 * dos componentes da GUI
	 */
	@Override
	public void registraEventos() {

		// Instanciando um novo listener
		MyActionListener myListener = new MyActionListener();

		/*
		 *  Adicionando o tratamento de acoes aos componentes da GUI
		 */
		this.frame.getBtnAbrirArquivo().addActionListener(myListener);
		this.frame.getBtnSair().addActionListener(myListener);
		this.frame.getBtnSobre().addActionListener(myListener);
		this.frame.getBtnPreencher().addActionListener(myListener);
		this.frame.getBtnLimpar().addActionListener(myListener);
	}

	/**
	 * Metodo responsavel por tratar as acoes do botao de abrir
	 * o arquivo da imagem
	 * 
	 * @return void
	 */
	public void abrirArquivo() {

		// Declaracao de variaveis locais
		MyFileChooser fileChooser = null;
		String retorno = "";
		int op = 0;

		try {
			// Desfazendo as acoes anteriores
			this.desfazerAcoesAnteriores();

			// Escolha do arquivo
			fileChooser = this.abrirFileChooser();
			op = fileChooser.lancarOpenDialog(this.frame);
			retorno = fileChooser.getArquivoSelecionado(op);

			if(!(retorno.equals(MyFileChooser.OPERACAO_CANCELADA) || 
					retorno.equals(MyFileChooser.OPERACAO_ERRO))
			) {				
				// Definindo o caminho do arquivo para aberturas posteriores
				this.caminhoUltimoArquivo = fileChooser.getSelectedFile().getAbsolutePath();

				/*
				 * Realizando a abertura do arquivo
				 * que contem o poligono 
				 */
				this.abrirArquivoImagem();

				/* 
				 * Constroi o poligono e adiciona ao painel
				 */
				this.desenharForma(
						this.construirPoligono(
								this.arquivoPoligono.getPontos()
						)
				);

				/*
				 *  Garantindo que o arquivo foi finalizado
				 */
				this.fecharArquivoImagem();
			}

		} catch(IOException e) {
			e.printStackTrace();
			Mensagem.mostraErro(
					this.frame, 
					"Falha na abertura do arquivo!"
			);
		} catch(NullPointerException e) {
			e.printStackTrace();
			Mensagem.mostraErro(
					this.frame, 
					"Falha na abertura de arquivo!"
			);
		} finally {
			this.frame.habilitarMenuPreenchimento();
		}
	}

	/**
	 * Metodo responsavel por realizar o preenchimento
	 * do poligono atraves das scanlines
	 * 
	 * @return void
	 */
	public void preencherPoligono() {

		// Declaracao das variaveis locais
		List<Reta> preenchimento = null;

		try {
			/*
			 * Definindo o posicionamento ds
			 * scanlines
			 */
			preenchimento = 
				Preenchimento.preenchePoligo(
						poligono, 
						this.getValorPasso()
				);

			/*
			 * Desenhando as scanlines no painel
			 */
			for (Reta reta: preenchimento) {
				this.desenharForma(reta.getPontos());
			}

		} catch(NumberFormatException e) {
			e.printStackTrace();
			Mensagem.mostraErro(
					this.frame, 
					e.getMessage()
			);
		} finally {
			this.frame.desabilitarMenuPreenchimento();
		}
	}

	/**
	 * Metodo responsavel por reinicializar o estado da interface para
	 * default
	 * 
	 * @return void
	 */
	public void limpar() {
		// Limpando as formas contidas no painel desenho
		this.frame.getPanelDesenho().setFormas(new ArrayList<Pontos>());

		// Desenhando o poligono novamente
		this.desenharForma(poligono.getPontos());

		this.frame.getBtnPreencher().setEnabled(false);
		this.frame.getBtnLimpar().setEnabled(false);

		this.frame.habilitarMenuPreenchimento();
	}

	/**
	 * Metodo responsavel por tratar as acoes do botao que mostra as informacoes
	 * sobre o(s) autor(es) deste projeto
	 * 
	 * @return void
	 */
	public void sobre() {
		Mensagem.mostraMensagemSobre(
				this.frame,
				"Universidade Federal de Itajuba\n" + 
				"CCT 720 - Computacao Grafica II \n" +
				"Professor: Dr. Edison Oliveira de Jesus\n" +
				"Aluno: Felipe Agostini Knappe - 12623"
		);
	}

	/**
	 * Metodo responsavel por tratar as acoes do botao 
	 * de finalizar o aplicativo
	 * 
	 * @return void
	 */
	public void sair() {
		if(Mensagem.confirmaMensagem(
				this.frame, 
				"Deseja realmente sair do programa?"
		)
		) {
			this.frame.dispose();
			System.exit(0);
		}
	}

	/* ****************************************************************************************************************************************************************
	 * 															Metodos auxiliares (privados)
	 ******************************************************************************************************************************************************************/

	/**
	 * Metodo responsavel por construir um poligono
	 * com os dados fornecidos no arquivo
	 * 
	 * @return void
	 */
	private Pontos construirPoligono(Pontos vertices) {
		poligono = new Poligono(vertices, Color.BLACK);
		return poligono.getPontos();
	}

	/**
	 * Metodo responsavel por adicionar as formas geometricas
	 * ao painel de desenho
	 * 
	 * @return void
	 */
	private void desenharForma(Pontos pontos) {		
		// Adiciona novas formas geometricas ao painel de desenho
		this.frame.getPanelDesenho().addForma(pontos);	
		this.frame.getPanelDesenho().repaint();
	}

	/**
	 * Metodo responsavel por retornar o valor do passo
	 * do preenchimento
	 * 
	 * @return int
	 */
	private int getValorPasso() throws NumberFormatException {
		// Declaracao de variaveis locais
		int passo = 0;

		// Determinando o valor do passo
		passo = 
			Mensagem.entradaDeDadosInteiros(
					"Determine o numero de passos para o preenchimento (1 a 10): "
			);

		if(passo < 1 || passo > 10) {
			throw new NumberFormatException("Entre com um numero inteiro entre 1 e 10!");
		}

		return passo;
	}

	/**
	 * Metodo responsavel por desfazer todas as formas
	 * geometricas anteiormente presentes no painel de
	 * desenho
	 * 
	 * @return void
	 */
	private void desfazerAcoesAnteriores() {
		this.frame.getPanelDesenho().setFormas(new ArrayList<Pontos>());
		this.frame.getPanelDesenho().repaint();
	}

	/**
	 * Metodo responsavel pela abertura da janela para escolha
	 * do arquivo de imagem
	 * 
	 * @return MyFileChooser
	 */
	private MyFileChooser abrirFileChooser() {

		// Declaracao das variaveis locais
		MyFileChooser fileChooser = null;

		// Inicializando o ficheiro de arquivos
		fileChooser = new MyFileChooser("Abrir arquivo do poligono");

		/*
		 *  Filtrando os arquivos a serem abertos somente para 
		 *  imagens em nivel de cinza
		 */
		fileChooser.filtro(".dat", "Arquivos de dados");
		return fileChooser;
	}

	/**
	 * Metodo responsavel por realizar a abertura dos arquivos
	 * da imagem
	 * 
	 * @throws IOException
	 */
	private void abrirArquivoImagem() throws IOException, IllegalArgumentException {
		// Definindo o novo arquivo de dados
		arquivoPoligono = new ArquivoPoligono(
				caminhoUltimoArquivo
		);
	}

	/**
	 * Metodo responsavel por fechar o stream para o arquivo
	 * da imagem
	 * 
	 * @return void
	 * @throws IOException
	 * @throws NullPointerException
	 */
	private void fecharArquivoImagem() throws IOException, NullPointerException {
		// Finalizando o stream com os arquivos
		if(!this.arquivoPoligono.isFechado()) {
			arquivoPoligono.fecharArquivo();
		}
	}	
}
